/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.toggletest;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataKeys;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;

import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ToggleTestAction extends AnAction {
    private static final String DEFAULT_EXTENSION = ".java";

    public void actionPerformed(AnActionEvent anActionEvent) {
        DataContext dataContext = anActionEvent.getDataContext();

        Project project = DataKeys.PROJECT.getData(dataContext);
        VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles();
        if (files != null && files.length > 0) {
            VirtualFile virtualFile = switchFile(files[0], project);
            if (virtualFile != null) {
                FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
                fileEditorManager.openFile(virtualFile, true);
            } else {
                Messages.showInfoMessage("Can't find file to swap with " + files[0].getPresentableName() +
                        ".  Check your configuration settings for ToggleTest.", "No Toggle Match");
            }
        }
    }

    private VirtualFile switchFile(VirtualFile file, Project project) {

        VirtualFile virtualFile = checkForSwap(file, project, false);
        if (virtualFile == null) {
            virtualFile = checkForSwap(file, project, true);
        }
        return virtualFile;
    }

    private VirtualFile checkForSwap(VirtualFile file, Project project, boolean reverse) {
        PsiManager psiManager = PsiManager.getInstance(project);
        PsiShortNamesCache shortNamesCache = psiManager.getShortNamesCache();
        ToggleTestConfiguration instance = ToggleTestConfiguration.getInstance(project);
        for (Iterator iterator = instance.iterator(); iterator.hasNext();) {
            ToggleRow entry = (ToggleRow) iterator.next();
            String from = reverse ? entry.getTo() : entry.getFrom();
            String to = reverse ? entry.getFrom() : entry.getTo();
            String swapFile = getAlternateFile(from, to, file);
            if (swapFile != null) {
                String swapName = swapFile.substring(swapFile.lastIndexOf('/') + 1);
                PsiFile[] toggleFiles;
                if (swapFile.endsWith(DEFAULT_EXTENSION)) {
                    String className = swapName.substring(0, swapFile.length() - DEFAULT_EXTENSION.length());
                    PsiClass[] classesByName = shortNamesCache.getClassesByName(className, GlobalSearchScope.allScope(project));
                    toggleFiles = getPsiFiles(classesByName);
                } else {
                    toggleFiles = shortNamesCache.getFilesByName(swapName);
                }
                if (toggleFiles != null && toggleFiles.length > 0) {
                    if (toggleFiles.length == 1) {
                        return toggleFiles[0].getVirtualFile();
                    }
                    return chooseToggleFile(toggleFiles, file, swapFile);
                }
            }
        }
        return null;
    }

    private VirtualFile chooseToggleFile(PsiFile[] toggleFiles, VirtualFile originalFile, String swapFile) {
        String originalPath = getParentPath(originalFile);
        int maxMatchLength = -1;
        VirtualFile ret = null;
        for (int i = 0; i < toggleFiles.length; i++) {
            VirtualFile toggleFile = toggleFiles[i].getVirtualFile();
            if (toggleFile != null) {
                String path = toggleFile.getPath();
                if (path.endsWith(swapFile)) {
                    int matchLength = getMatchLength(originalPath, getParentPath(toggleFile));
                    if (matchLength > maxMatchLength) {
                        maxMatchLength = matchLength;
                        ret = toggleFile;
                    }
                }
            }
        }
        return ret;
    }

    private String getParentPath(VirtualFile originalFile) {
        VirtualFile parent = originalFile.getParent();
        return parent == null ? "" : parent.getPath();
    }

    private int getMatchLength(String original, String match) {
        int len = Math.min(original.length(), match.length());
        for (int i = 0; i < len; i++) {
            if (original.charAt(original.length() - i - 1) != match.charAt(match.length() - i - 1)) {
                return i;
            }

        }
        return len;
    }

    private PsiFile[] getPsiFiles(PsiClass[] classesByName) {
        PsiFile[] ret = new PsiFile[classesByName.length];
        for (int i = 0; i < classesByName.length; i++) {
            ret[i] = classesByName[i].getContainingFile();
        }
        return ret;
    }

    private String getAlternateFile(String from, String to, VirtualFile file) {
        if (from == null || from.length() == 0 || to == null || to.length() == 0) {
            return null;
        }
        String name = file.getPath();
        String regexp = addExtensionIfNeeded(from).replace("*", "(\\w+)").replace("\\.", "\\\\.") + "$";
        Pattern pattern = Pattern.compile(regexp);
        Matcher matcher = pattern.matcher(name);
        if (matcher.find()) {
            String ret = addExtensionIfNeeded(to);
            if (matcher.groupCount() > 0) {
                ret = ret.replace("*", matcher.group(1));
            }
            return ret;
        }
        return null;
    }

    private String addExtensionIfNeeded(String from) {
        if (from.indexOf('.') == -1) {
            return from + DEFAULT_EXTENSION;
        }
        return from;
    }

}
